package com.jady.main;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;

import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.event.VMDeathEvent;
import com.sun.jdi.event.VMDisconnectEvent;
import com.sun.jdi.request.ClassPrepareRequest;
import com.sun.jdi.request.EventRequestManager;
import com.sun.jdi.request.MethodEntryRequest;

public class TraceIt {

	// public static final String FULL_CLASS_NAME = "com.jady.main.TestJad";
	// public static final String PKG_NAME = "com.jady.*";

	public static final String PKG_NAME = "com.jady.*";

	public static void main(String[] args) throws IOException,
			AbsentInformationException, InterruptedException {

		LinkedBlockingQueue<EventSet> queue = new LinkedBlockingQueue<EventSet>();
		EventHandlerThread handler = new EventHandlerThread(queue);
		Thread thread = new Thread(handler);
		thread.start();
		
		// connect local TestJad
		// VirtualMachine vm = new VMAcquirer().connect(8000);
		VirtualMachine vm = new VMAcquirer().connect(8000);

		// set watch field on already loaded classes
		List<ReferenceType> referenceTypes = vm.classesByName(PKG_NAME);
		for (ReferenceType refType : referenceTypes) {
			addMethodEntryReq(vm, refType);
		}
		// watch for loaded classes
		addClassWatch(vm);

		// resume the vm
		vm.resume();

		// process events
		EventQueue eventQueue = vm.eventQueue();
		while (true) {
			EventSet eventSet = eventQueue.remove();

			for (Event event : eventSet) {
				if (event instanceof VMDeathEvent
						|| event instanceof VMDisconnectEvent) {
					// exit
					queue.put(eventSet);
					return;
				} else if (event instanceof ClassPrepareEvent) {
					// watch field on loaded class
					ClassPrepareEvent classPrepEvent = (ClassPrepareEvent) event;
					ReferenceType refType = classPrepEvent.referenceType();
					// System.out.println("Class refered: "+classPrepEvent.referenceType().getClass().getName());
					addMethodEntryReq(vm, refType);
				}
			}

			queue.put(eventSet);
			eventSet.resume();
		}
	}

	private static void addClassWatch(VirtualMachine vm) {
		EventRequestManager erm = vm.eventRequestManager();
		ClassPrepareRequest classPrepareRequest = erm
				.createClassPrepareRequest();
		classPrepareRequest.addClassFilter(PKG_NAME);
		classPrepareRequest.setEnabled(true);
	}

	private static void addMethodEntryReq(VirtualMachine vm,
			ReferenceType refType) throws AbsentInformationException {

		EventRequestManager erm = vm.eventRequestManager();
		MethodEntryRequest methEntReq = erm.createMethodEntryRequest();
		methEntReq.addClassFilter(PKG_NAME); // setting the filter, com.mypkg.*
												// will work
		methEntReq.setEnabled(true);

	}

}
